//
//  WaterFallView.m
//  CommonUI
//
//  Created by winston on 12-10-9.
//  Copyright (c) 2012年 appfactory. All rights reserved.
//

#import "WaterFallView.h"

@implementation WaterFallCellInfo

@synthesize frame,index,column,reuseID;//cell;

- (BOOL)isEqual:(id)object
{
    if (![object isKindOfClass:[WaterFallCellInfo class]]) return NO;
    return index == [object index];
}

- (NSUInteger)hash
{
    return index;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"<%@: index: %d column:%d>",NSStringFromClass([self class]), index,column];
}

-(void)dealloc
{
    [reuseID release];
    [super dealloc];
}

@end

@interface WaterFallView()

- (NSSet *)getVisibleCellInfo;
- (void)layoutCellWithCellInfo:(WaterFallCellInfo *)info;

@property (nonatomic,assign) NSSet *visibleCellInfo;
@property (nonatomic,assign) NSMutableDictionary *cellCache;

@end

@implementation WaterFallView

@synthesize columnMargin;
@synthesize cellMargin;
@synthesize wfDelegate;
@synthesize visibleCellInfo;
@synthesize cellCache;

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if(self)
    {
        columnMargin = DEFAULTCOLUMNTMARGIN;
        cellMargin = DEFAULTROWMARGIN;
        cellCount = 0;
        cellCache = [[NSMutableDictionary alloc]initWithCapacity:5];
        infoForCells = [NSMutableArray new];
        
        self.delegate = self;
    }
    return self;
}

- (void)reloadData
{
    [cellCache removeAllObjects];
    [infoForCells removeAllObjects];
    
    if(headerView){[headerView removeFromSuperview];headerView = nil;}
    if(footerView){[footerView removeFromSuperview];footerView = nil;}
    if(colsMaxHeight != NULL) {free(colsMaxHeight); colsMaxHeight = NULL;}
    if(colsOriginX != NULL)   {free(colsOriginX); colsOriginX = NULL;}

    if ([wfDelegate respondsToSelector:@selector(headerForWaterFallView:)])
        headerView = [wfDelegate headerForWaterFallView:self];
    
    if ([wfDelegate respondsToSelector:@selector(footerForWaterFallView:)])
        footerView = [wfDelegate footerForWaterFallView:self];
    
    // calculate height for all cells
    NSInteger numberOfCells = [wfDelegate numberOfCellsInWaterFallView:self];
    NSInteger numberOfColumns = [wfDelegate numberOfColumnsInWaterFallView:self];

    cellCount = numberOfCells;
    columnWidth = (int)((self.bounds.size.width - (numberOfColumns + 1) * self.columnMargin) / numberOfColumns);
    
    if (numberOfColumns < 1)
        [NSException raise:NSInvalidArgumentException format:@"columns number < 1!"];
    
    colsMaxHeight = calloc(numberOfColumns, sizeof(CGFloat));
    colsOriginX = calloc(numberOfColumns, sizeof(CGFloat));
    
    if (colsMaxHeight == NULL || colsOriginX == NULL)
        [NSException raise:NSMallocException format:@"allocating memory error."];
    
    
    CGFloat cellHeight = headerView ? cellMargin+headerView.frame.size.height + cellMargin : cellMargin;
    
    for (int i = 0; i < numberOfColumns; i++)
    {
        colsOriginX[i] = (i == 0 ? columnMargin : colsOriginX[i - 1] + columnWidth + columnMargin);
        colsMaxHeight[i] = cellHeight;
    }
    
    for (int i = 0; i < numberOfCells; i++)
    {
        CGFloat height = [wfDelegate waterFallView:self heightForCellAtIndex:i];
        
        NSUInteger shortestCol = 0;
        for (int j = 1; j < numberOfColumns; j++)
        {
            if (colsMaxHeight[j] < colsMaxHeight[shortestCol] - 1.0f)
                shortestCol = j;
        }
    
        WaterFallCellInfo *info = [WaterFallCellInfo new];
        info.frame = CGRectMake(colsOriginX[shortestCol], colsMaxHeight[shortestCol], columnWidth, height);
        info.index = i;
        info.column = shortestCol;
        [infoForCells addObject:info];
        [info release];
        
        colsMaxHeight[shortestCol] += height + cellMargin;
    }
    
    CGFloat maxHeight = 0;
    for (int i = 0; i < numberOfColumns; i++)
    {
        if (colsMaxHeight[i] > maxHeight)
            maxHeight = colsMaxHeight[i];
    }
    
    // 清除旧的View
    for(UIView * view in self.subviews)
    {
       if([view conformsToProtocol:@protocol(WaterFallCellDelegate)])
         [view removeFromSuperview];
    }
    
    if (visibleCellInfo) {
        [visibleCellInfo release];
    }
    
    visibleCellInfo = [self getVisibleCellInfo];
    for (WaterFallCellInfo *info in visibleCellInfo)
    {
        [self layoutCellWithCellInfo:info];
    }
    
    if(headerView)
    {
        [self addSubview:headerView];
        [headerView release];
    }
    
    if(footerView)
    {
        footerView.frame = CGRectMake(columnMargin, maxHeight, footerView.frame.size.width, footerView.frame.size.height);
        [self addSubview:footerView];
        [footerView release];
        maxHeight += footerView.bounds.size.height + cellMargin;
    }

    self.contentSize = CGSizeMake(0.0f, maxHeight<self.frame.size.height?self.frame.size.height+5:maxHeight);
}

- (void)appendData
{
    NSInteger numberOfCells = [wfDelegate numberOfCellsInWaterFallView:self];
    if(cellCount==0||cellCount>=numberOfCells)  //第一次加载需使用reloadData
        return;
   
    NSInteger numberOfColumns = [wfDelegate numberOfColumnsInWaterFallView:self];
    
    for (int i = cellCount; i < numberOfCells; i++)
    {
        CGFloat height = [wfDelegate waterFallView:self heightForCellAtIndex:i];
        
        NSUInteger shortestCol = 0;
        for (int j = 1; j < numberOfColumns; j++)
        {
            if (colsMaxHeight[j] < colsMaxHeight[shortestCol] - 0.5f)
                shortestCol = j;
        }
        
        WaterFallCellInfo *info = [WaterFallCellInfo new];
        info.frame = CGRectMake(colsOriginX[shortestCol], colsMaxHeight[shortestCol], columnWidth, height);
        info.index = i;
        info.column = shortestCol;
        [infoForCells addObject:info];
        [info release];
        
        colsMaxHeight[shortestCol] += height + cellMargin;
    }
    
    cellCount  = numberOfCells;
    // determine the visible cells' range
    
    NSSet *newVisibleCellInfo = [self getVisibleCellInfo];
    
    for (WaterFallCellInfo *info in visibleCellInfo)
    {
        if (![newVisibleCellInfo containsObject:info])
        {
            NSString *cellID = info.reuseID;
            NSMutableArray *cellArray = [self.cellCache objectForKey:cellID];
            if (cellArray == nil)
            {
                cellArray = [NSMutableArray arrayWithCapacity:10];
                [cellCache setObject:cellArray forKey:cellID];
            }
    
            UIView * needCachedView = [self viewWithTag:info.index+TAGSEED];
            if(needCachedView&&needCachedView.superview==self)
            {
                [cellArray addObject:needCachedView];
                [needCachedView removeFromSuperview];
            }
        }
    }
    
    for (WaterFallCellInfo *info in newVisibleCellInfo)
    {
        if (![visibleCellInfo containsObject:info])
        {
            [self layoutCellWithCellInfo:info];
        }
    }
    
    [visibleCellInfo release];
    visibleCellInfo = newVisibleCellInfo;
    
    CGFloat maxHeight = 0;
    for (int i = 0; i < numberOfColumns; i++)
    {
        if (colsMaxHeight[i] > maxHeight)
            maxHeight = colsMaxHeight[i];
    }
    
    if(footerView)
    {
        footerView.frame = CGRectMake(columnMargin, maxHeight, footerView.frame.size.width, footerView.frame.size.height);
        [self addSubview:footerView];
        maxHeight += footerView.bounds.size.height + cellMargin;
    }
    else
    {
        maxHeight+=cellMargin;
    }
    
    self.contentSize = CGSizeMake(0.0f, maxHeight<self.frame.size.height?self.frame.size.height+5:maxHeight);
}

- (id<WaterFallCellDelegate>)dequeueReusableCellWithIdentifier:(NSString *)identifier
{
    NSMutableArray *cellArray = [cellCache objectForKey:identifier];
    
    id<WaterFallCellDelegate> cell = nil;
    if ([cellArray count] > 0)
    {
        cell = [[cellArray lastObject]retain];
        [cellArray removeLastObject];
    }
    return cell;
}

- (NSSet *)getVisibleCellInfo
{
    CGFloat offsetTop = self.contentOffset.y;
    CGFloat offsetBottom = offsetTop + self.bounds.size.height;
    
    NSMutableSet *ret = [[NSMutableSet alloc]initWithCapacity:10];
    
    for (WaterFallCellInfo * info in infoForCells)
    {
        CGFloat top = info.frame.origin.y;
        CGFloat bottom = CGRectGetMaxY(info.frame);
            
        if (bottom < offsetTop)
        {
            continue;
        }
        else if (top > offsetBottom)
        {
            continue;
        }
        else
        {
            [ret addObject:info];
        }
    }
    return ret;
}

- (void)layoutCellWithCellInfo:(WaterFallCellInfo *)info
{    
    UIView<WaterFallCellDelegate> *cell = [wfDelegate waterFallView:self cellAtIndex:info.index];
    cell.frame = info.frame;
    cell.tag = info.index+TAGSEED;
    info.reuseID = cell.reuseIdentifier;
    
    if ([wfDelegate respondsToSelector:@selector(waterFallView:willDisplayCell:forIndex:)])
        [wfDelegate waterFallView:self willDisplayCell:cell forIndex:info.index];

    [self addSubview:cell];
}

-(void)dealloc
{
    [infoForCells release];
    [cellCache release];
    [super dealloc];
}

#pragma UIScrollDelegate Methods

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidScroll:)])
        [wfDelegate scrollViewDidScroll:self];
    
    if(scrollView.contentOffset.y<0) return;
    
    NSSet *newVisibleCellInfo = [self getVisibleCellInfo];
    
    for (WaterFallCellInfo *info in visibleCellInfo)
    {
        if (![newVisibleCellInfo containsObject:info])
        {
            NSString *cellID = info.reuseID;
            NSMutableArray *cellArray = [self.cellCache objectForKey:cellID];
            if (cellArray == nil)
            {
                cellArray = [NSMutableArray arrayWithCapacity:10];
                [cellCache setObject:cellArray forKey:cellID];
            }
            
            UIView* needCachedView = [self viewWithTag:info.index+TAGSEED];
            if(needCachedView&&needCachedView.superview==self)
            {
                [cellArray addObject:needCachedView];
                [needCachedView removeFromSuperview];
                
                if ([wfDelegate respondsToSelector:@selector(waterFallView:didBackupCell:forIndex:)])
                    [wfDelegate waterFallView:self didBackupCell:(UIView<WaterFallCellDelegate>*)needCachedView forIndex:info.index];
            }
        }
    }
    
    for (WaterFallCellInfo *info in newVisibleCellInfo)
    {
        if (![visibleCellInfo containsObject:info])
        {
            [self layoutCellWithCellInfo:info];
        }
    }
    
    [visibleCellInfo release];
    visibleCellInfo = newVisibleCellInfo;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidZoom:)])
        [wfDelegate scrollViewDidZoom:self];
}


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)])
        [wfDelegate scrollViewWillBeginDragging:self];
}


- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)])
        [wfDelegate scrollViewWillEndDragging:self withVelocity:velocity targetContentOffset:targetContentOffset];
}


- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)])
        [wfDelegate scrollViewDidEndDragging:self willDecelerate:decelerate];
}


- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)])
        [wfDelegate scrollViewWillBeginDecelerating:self];
}


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)])
        [wfDelegate scrollViewDidEndDecelerating:self];
}


- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)])
        [wfDelegate scrollViewDidEndScrollingAnimation:self];    
}


- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(viewForZoomingInScrollView:)])
        return [wfDelegate viewForZoomingInScrollView:self];
    else
        return nil;
}


- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)])
        [wfDelegate scrollViewWillBeginZooming:self withView:view];
}


- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)])
        [wfDelegate scrollViewDidEndZooming:self withView:view atScale:scale];
}


- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)])
        return [wfDelegate scrollViewShouldScrollToTop:self];
    else
        return YES;
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
    if ([wfDelegate respondsToSelector:@selector(scrollViewDidScrollToTop:)])
        [wfDelegate scrollViewDidScrollToTop:self];
}

@end
